#include <iostream>
#include <httpserver.hpp>
#include <unistd.h>
#include <iostream>
#include <string>
#include <vector>

#include "httpclient.h"
#include "utils.h"
#include "stream_reader_ffmpeg.h"

using namespace httpserver;


//mutex
pthread_mutex_t lock;
//
IniParser* g_iniparser;
HttpClient* client = new HttpClient();
std::vector<std::shared_ptr<StreamReaderFFmpeg>> g_readers(0);
std::string gatewayhost;


class recvhost_management : public http_resource {
public:
    const std::shared_ptr<http_response> render(const http_request& req) {

        //json resp
        boost::property_tree::ptree json;
        json.put<int>("code", 200);
        json.put("msg", "success");
        json.put("recvhost", gatewayhost);

        std::string resp = json2string(json);
        return std::shared_ptr<http_response>(new string_response(resp));
    }
    const std::shared_ptr<http_response> render_POST(const http_request& req) {
        std::string content = req.get_content();

        auto pt = string2json(content);

        std::string gateway_server = pt.get<std::string>("ip");
        std::string gateway_port = pt.get<std::string>("port");
        std::string gateway_url = pt.get<std::string>("uri");

        pthread_mutex_lock(&lock); //lock
        gatewayhost = std::string("http://");
        gatewayhost.append(gateway_server).append(":").append(gateway_port).append("/").append(gateway_url);
        pthread_mutex_unlock(&lock); //unlock

        std::cout << "[INFO]: Change recvhost to --> " << gatewayhost << std::endl;

        g_iniparser->PutString("server", gateway_server.c_str(), "gateway");
        g_iniparser->PutString("port", gateway_port.c_str(), "gateway");
        g_iniparser->PutString("url", gateway_url.c_str(), "gateway");

        //json resp
        boost::property_tree::ptree json;
        json.put<int>("code", 200);
        json.put("msg", "success");

        std::string resp = json2string(json);
        return std::shared_ptr<http_response>(new string_response(resp));
    }
};


class camera_management : public http_resource {
public:
    const std::shared_ptr<http_response> render(const http_request& req) {

        pthread_mutex_lock(&lock); //lock
        int reader_size = g_readers.size();
        pthread_mutex_unlock(&lock); //unlock

        //json
        boost::property_tree::ptree json, rejson;
        rejson.put<int>("code", 200);
        rejson.put("msg", "success");

        pthread_mutex_lock(&lock); //lock
        if (reader_size > 0) {
            for (int i = 0; i < g_readers.size(); i++) {
                std::string rstp = g_readers[i]->GetUrl();
                int camid = g_readers[i]->GetCamid();

                boost::property_tree::ptree tjson;
                tjson.put<int>("camid", camid);
                tjson.put("rtsp", rstp);
                json.push_back(std::make_pair("",tjson));
            }
        }
        pthread_mutex_unlock(&lock); //unlock

        rejson.add_child("camlist", json);
        std::string resp = json2string(rejson);
        return std::shared_ptr<http_response>(new string_response(resp));
    }
    const std::shared_ptr<http_response> render_POST(const http_request& req) {
        std::string content = req.get_content();

        auto pt = string2json(content);
        int camid = pt.get<int>("camid");
        const std::string rtsp = pt.get<std::string>("rtsp");
        const std::string op = pt.get<std::string>("op");

        //json resp
        boost::property_tree::ptree json;
        json.put<int>("code", 200);
        json.put("msg", "success");


        //parse
        bool isOpened = false;
        int srIdx = -1;

        pthread_mutex_lock(&lock); //lock
        for (int i = 0; i < g_readers.size(); i++) {
            if (rtsp == g_readers[i]->GetUrl()) {
                isOpened = true;
                srIdx = i;
                break;
            }
        }
        pthread_mutex_unlock(&lock); //unlock

        if (op == "open") {
            if (!isOpened) {
                std::shared_ptr<StreamReaderFFmpeg> rtspreader(new StreamReaderFFmpeg(rtsp, camid));
                if (rtspreader->init()<0) {
                    json.put<int>("code", 400);
                    json.put("msg", "failure");
                    rtspreader->release(); //note: just can use for init failure!
                } else {
                    rtspreader->Run();
                    pthread_mutex_lock(&lock); //lock
                    g_readers.push_back(rtspreader);
                    pthread_mutex_unlock(&lock); //unlock
                    std::cout << "[INFO]: Opened New Stream: " << rtspreader->GetUrl() << " Left: " << g_readers.size() << std::endl;
                }
            }
        }
        else if (op == "close") {
            if (isOpened) {
                std::shared_ptr<StreamReaderFFmpeg> ptr = g_readers[srIdx];
                ptr->Stop();
                std::cout << "[INFO]: Closed Stream: " << ptr->GetUrl() << " Left: " << g_readers.size() << std::endl;
            }
        }


	std::string resp = json2string(json);
        return std::shared_ptr<http_response>(new string_response(resp));
    }
};


void *thread_post(void* arg)
{
    std::string gateway_server = g_iniparser->GetString("server", "gateway");
    std::string gateway_port = g_iniparser->GetString("port", "gateway");
    std::string gateway_url = g_iniparser->GetString("url", "gateway");
    gatewayhost = std::string("http://");
    gatewayhost.append(gateway_server).append(":").append(gateway_port).append("/").append(gateway_url);
    std::cout << gatewayhost << std::endl;

    while (true) {

        pthread_mutex_lock(&lock); //lock
        int reader_size = g_readers.size();
        pthread_mutex_unlock(&lock); //unlock

        if (reader_size > 0) {
            for (int i = 0; i < g_readers.size(); i++) {

                int camid = -1;
                cv::Mat image;

                if (g_readers[i]->isStoped()) {
                    pthread_mutex_lock(&lock); //lock
                    g_readers.erase(g_readers.begin()+i);
                    g_readers.shrink_to_fit();
                    pthread_mutex_unlock(&lock); //unlock
                    std::cout << "streamer size: " << g_readers.size() << std::endl;
                    continue;
                }

                camid = g_readers[i]->GetCamid();
                image = g_readers[i]->GetCurFrame();

                if (image.empty()) {
                    continue;
                }

                //json
                boost::property_tree::ptree json;

                std::string simg = Mat2Base64(image, "jpg");
                json.put<int>("camid",camid);
                json.put<std::string>("image", simg);

                ////post
                std::string post = json2string(json);

                pthread_mutex_lock(&lock); //lock
                std::string host = gatewayhost;
                pthread_mutex_unlock(&lock); //unlock

                std::string resp;
                int code = client->Post(host, post, resp);
                std::cout << "[INFO]: post. @@@@ [camid]: " << camid << " [host]: " << host << std::endl;
            }
        }
    }
}


int main(int argc, char** argv) {

    //ini parser
    g_iniparser = new IniParser();
    int port = g_iniparser->GetFloat("port", "setting");

    //init mutex
    pthread_mutex_init(&lock,NULL);


    //holding threads
    pthread_t thd1;
    pthread_create(&thd1, NULL, thread_post, NULL);
    pthread_detach(thd1);


    //httpserver
    webserver ws = create_webserver(port);
    std::cout << "Http Server is Running on: " << port << std::endl;

    camera_management cm;
    recvhost_management rm;
    ws.register_resource("/server/management", &cm);
    ws.register_resource("/management/recvhost", &rm);

    ws.start(true);


    //destroy mutex
    pthread_mutex_destroy(&lock);
    return 0;
}

